home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / input.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  20KB  |  643 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) input.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: input.c,v 1.4 88/07/20 15:03:51 shebs Exp $ */
  8.  
  9. /* To accommodate simultaneous movement by several players, our setup must */
  10. /* be a little elaborate.  Basically, the program keeps the initiative; */
  11. /* when a side's movement phase needs input, it posts a "request" and a */
  12. /* handler for that request.  When X gets an event, data about it is */
  13. /* preprocessed and then stuffed into the request structure.  At the end */
  14. /* of a movement subphase, the requests are fulfilled by calling the handler */
  15. /* and passing it the side and a pointer to the request. */
  16.  
  17. /* The bad part about all this is that *every* *single* interaction must */
  18. /* be handled this way - no more calling a routine to get a value and */
  19. /* waiting until it is complete! :-( */
  20.  
  21. #include "config.h"
  22. #include "misc.h"
  23. #include "dir.h"
  24. #include "period.h"
  25. #include "side.h"
  26. #include "unit.h"
  27. #include "global.h"
  28. #include "map.h"
  29.  
  30. /* Magic values (unlikely to show up as player input) */
  31.  
  32. #define DFLTFLAG (-12345)       /* flags when default arg to be used */
  33. #define NEGFLAG  (-12346)       /* flags when default arg to be used */
  34. #define ITERFLAG (-12347)       /* flags args that are "itertime" */
  35.  
  36. #define ESCAPE '\033'           /* standard abort character */
  37. #define BACKSPACE '\010'        /* for fixing strings being entered */
  38.  
  39. /* The following structure is used only for the dispatch table. */
  40.  
  41. typedef struct func_tab {
  42.     char fchar;                 /* character to match against */
  43.     int (*code)();              /* pointer to command's function */
  44.     int defaultarg;             /* default for argument */
  45.     bool needunit;              /* true if cmd always applies to a unit */
  46.     char *help;                 /* short documentation string */
  47. } FuncTab;
  48.  
  49. extern int giventime;           /* flags use of chess clock */
  50.  
  51. /* Predeclarations of all commands. */
  52.  
  53. int do_sentry(), do_delay(), do_wakeup(), do_wakemain(), do_embark(),
  54.     do_exit(), do_resign(), do_message(), do_follow(),
  55.     do_redraw(), do_flash(), do_disband(), do_return(),
  56.     do_sit(), do_product(), do_idle(), do_help(), do_version(),
  57.     do_save(), do_standing(), do_ident(), do_moveto(), do_coast(),
  58.     do_survey_mode(), do_unit_info(), do_printables(), do_occupant(),
  59.     do_options(), do_name(), do_unit(), do_give_unit(), do_center(),
  60.     do_mark_unit(), do_add_player(), do_patrol(), do_give(), do_take();
  61.  
  62. char dirchars[] = DIRCHARS;     /* typable characters for directions */
  63.  
  64. /* The command table itself.  Default iterations of ITERFLAG actually turn */
  65. /* into the value of "itertime", which can be set by options cmd. */
  66.  
  67. FuncTab commands[] = {
  68.     's', do_sentry, ITERFLAG, TRUE, "put a unit on sentry duty",
  69.     'w', do_wakemain, 0, FALSE, "wake units up from whatever they were doing",
  70.     ' ', do_sit, 1, FALSE, "(Space Bar) make unit be on sentry this turn only",
  71.     'r', do_return, 1, TRUE, "return unit to nearest city/transport",
  72.     'd', do_delay, 1, TRUE, "delay unit's move until later in turn",
  73.     'f', do_follow, ITERFLAG, TRUE, "follow a designated leader",
  74.     'm', do_moveto, 1, TRUE, "move to given location",
  75.     'z', do_survey_mode, 1, FALSE, "toggle between survey and move modes",
  76.     'a', do_occupant, 1, TRUE, "look at the occupants",
  77.     'e', do_embark, 1, TRUE, "embark units onto transport occupying same unit",
  78.     'x', do_mark_unit, 1, TRUE, "mark unit for later reference",
  79.     'g', do_give, -1, TRUE, "give supplies to the transport",
  80.     't', do_take, -1, TRUE, "take supplies from the transport",
  81.     'c', do_center, 1, FALSE, "designate current location as center of action",
  82.     'o', do_options, 5, FALSE, "set various options",
  83.     'p', do_printables, 1, FALSE, "put various data into files for printing",
  84.     'v', do_flash, 1, FALSE, "highlight current position",
  85.     ' ', NULL, 0, 0, "",
  86.     'P', do_product, 1, TRUE, "set/change unit production",
  87.     'I', do_idle, ITERFLAG, TRUE, "set unit to not produce anything",
  88.     'W', do_wakeup, 0, FALSE, "wake ALL units in area, including occupants",
  89.     'D', do_disband, 1, TRUE, "disband a unit and send it home",
  90.     'F', do_coast, ITERFLAG, TRUE, "follow a coastline",
  91.     'Z', do_patrol, ITERFLAG, TRUE, "go on a patrol",
  92.     'O', do_standing, 1, TRUE, "set standing orders for occupants",
  93.     'G', do_give_unit, -2, TRUE, "turn unit over to another side",
  94.     'M', do_message, -2, FALSE, "send a message to other sides",
  95.     'C', do_name, 1, FALSE, "call a unit or side by a name",
  96.     'X', do_resign, -2, FALSE, "resign from the game",
  97.     'Q', do_exit, 1, FALSE, "kill game for all players",
  98.     'S', do_save, 1, FALSE, "save the game into a file",
  99.     'A', do_add_player, 1, FALSE, "add a new player to the game",
  100.     '?', do_help, 1, FALSE, "display help info",
  101.     '/', do_ident, 1, FALSE, "identify things on screen",
  102.     '=', do_unit_info, 1, FALSE, "display details about a type of unit",
  103.     'V', do_version, 1, FALSE, "display program version",
  104.     '\\', do_unit, 1, FALSE, "build a new unit (Build mode only)",
  105.     '\022', do_redraw, 1, FALSE, "(^R) redraw screen",
  106.     '\014', do_redraw, 1, FALSE, "(^L) redraw screen",
  107.     '\0', NULL, 0, FALSE, NULL   /* end of table marker */
  108. };
  109.  
  110. /* Just to get everything off on the right foot. */
  111.  
  112. init_requests(side)
  113. Side *side;
  114. {
  115.     side->reqvalue = DFLTFLAG;
  116. }
  117.  
  118. /* Post a request - need only know the handler that will be called to */
  119. /* fulfill the request later, and sometimes a relevant unit. */
  120.  
  121. request_input(side, unit, handler)
  122. Side *side;
  123. Unit *unit;
  124. int (*handler)();
  125. {
  126.     if (Debug) printf("Making %s request ", side->name);
  127.     if (!humanside(side)) fprintf(stderr, "Gack!\n");
  128.     if (!side->reqactive) {
  129.     if (Debug) printf("(accepted)\n");
  130.     side->reqactive = TRUE;
  131.     side->reqhandler = handler;
  132.     side->reqtype = GARBAGE;
  133.     side->requnit = unit;
  134.     /* any other data slots are setup by callers of this routine */
  135.     } else {
  136.     if (Debug) printf("(ignored)\n");
  137.     }
  138. }
  139.  
  140. /* Make a request go away.  Careful about using this! */
  141.  
  142. cancel_request(side)
  143. Side *side;
  144. {
  145.     if (side->reqactive) {
  146.     if (Debug) printf("Canceling %s request\n", side->name);
  147.     side->reqactive = FALSE;
  148.     erase_cursor(side);
  149.     clear_info(side);
  150.     flush_output(side);
  151.     }
  152. }
  153.  
  154. /* Handle all requests at once.  This routine *will* wait forever for */
  155. /* input (polling is evil).  This is also the right place to draw the */
  156. /* unit cursor, since it is unwanted unless we are waiting on input. */
  157.  
  158. handle_requests()
  159. {
  160.     bool waiters = FALSE;
  161.     Side *side;
  162.  
  163.     for_all_sides(side) {
  164.     if (side->reqactive) {
  165.         waiters = TRUE;
  166.         /* wasteful to call this for everybody... */
  167.         if (side->info_change) {
  168.             clear_info(side);
  169.             show_info(side);
  170.             side->info_change = FALSE;
  171.         }
  172.         if (side->cursor_change) {
  173.             erase_cursor(side);
  174.             draw_cursor(side);
  175.             side->cursor_change = FALSE;
  176.         }
  177.     }
  178.     }
  179.     if (waiters) {
  180.     get_input();
  181.     for_all_sides(side) {
  182.         if (side->reqactive) {
  183.         if (side->reqtype != GARBAGE) {
  184.             if (Debug) printf("Answering %s request with inptype %d\n",
  185.                       side->name, side->reqtype);
  186.             side->reqactive = FALSE;
  187.             (*(side->reqhandler))(side);
  188.         }
  189.         /*erase_cursor(side);*/
  190.         /*clear_info(side);*/
  191.         flush_output(side);
  192.         }
  193.     }
  194.     }
  195. }
  196.  
  197. /* Acquire a command from the player.  Command may be prefixed with a */
  198. /* nonnegative number which is given as an argument to the command function */
  199. /* (otherwise arg defaults to value in third column of command table). */
  200. /* This routine takes in both keyboard and mouse input, other kinds of */
  201. /* devices should be handled here also (rriiiight...).  Don't do any of */
  202. /* this if the clock ran out, just supply an innocuous command. */
  203.  
  204. x_command(side)
  205. Side *side;
  206. {
  207.     int dir, terr, sign = 1;
  208.  
  209.     switch (side->reqtype) {
  210.     case KEYBOARD:
  211.     if (Debug) printf("%s keyboard input: %c (%d)\n",
  212.               side->name, side->reqch, side->reqvalue);
  213.     if (isdigit(side->reqch)) {
  214.         if (side->reqvalue == DFLTFLAG) {
  215.         side->reqvalue = 0;
  216.         } else if (side->reqvalue == NEGFLAG) {
  217.         side->reqvalue = 0;
  218.         sign = -1;
  219.         } else {
  220.         side->reqvalue *= 10;
  221.         }
  222.         side->reqvalue +=
  223.         (side->reqvalue >= 0 ? 1 : -1) * (side->reqch - '0');
  224.         side->reqvalue *= sign;
  225.         sprintf(side->promptbuf, "Arg: %d", side->reqvalue);
  226.         show_prompt(side);
  227.         request_input(side, side->curunit, x_command);
  228.         return;
  229.     } else {
  230.         clear_prompt(side);
  231.         if (Build && ((terr = find_terr(side->reqch)) >= 0)) {
  232.         if (side->reqvalue == DFLTFLAG) side->reqvalue = 0;
  233.         do_terrain(side, terr, side->reqvalue);
  234.         } else if (side->reqch == '-') {
  235.         side->reqvalue = NEGFLAG;
  236.         request_input(side, side->curunit, x_command);
  237.         return;
  238.         } else if ((dir = iindex(side->reqch, dirchars)) >= 0) {
  239.         if (side->reqvalue == DFLTFLAG) side->reqvalue = 1;
  240.         do_dir(side, dir, side->reqvalue);
  241.         } else if ((dir = iindex(lowercase(side->reqch), dirchars)) >= 0) {
  242.         if (side->reqvalue == DFLTFLAG)
  243.             side->reqvalue = side->itertime;
  244.         if (side->mode == SURVEY) side->reqvalue = 10;
  245.         do_dir(side, dir, side->reqvalue);
  246.         } else {
  247.         execute_command(side, side->reqch, side->reqvalue);
  248.         }
  249.     }
  250.     break;
  251.     case MAPPOS:
  252.     if (Debug) printf("%s map input: %d,%d (%d)\n",
  253.               side->name, side->reqx, side->reqy, side->reqvalue);
  254.     clear_prompt(side);
  255.     if (side->reqx == side->curx && side->reqy == side->cury) {
  256.         if (side->curunit && side->mode == MOVE)
  257.          do_sit(side, 1);
  258.         break;
  259.     } else {
  260.         if (side->teach) {
  261.         cache_moveto(side, side->reqx, side->reqy);
  262.         } else {
  263.         switch (side->mode) {
  264.         case MOVE:
  265.             if (side->curunit) {
  266.             order_moveto(side->curunit, side->reqx, side->reqy);
  267.             side->curunit->orders.flags &= ~SHORTESTPATH;
  268.             side->info_change = TRUE;
  269.             }
  270.             break;
  271.         case SURVEY:
  272.             move_survey(side, side->reqx, side->reqy);
  273.             break;
  274.         default:
  275.             case_panic("mode", side->mode);
  276.         }
  277.         }
  278.     }
  279.     break;
  280.     default:
  281.     break;
  282.     }
  283.     if (giventime && !side->timedout && side->timeleft <= 0) {
  284.     side->timedout = TRUE;
  285.     notify(side, "You ran out of time!!");
  286.     beep(side);
  287.     }
  288.     /* Reset the arg so we don't get confused next time around */
  289.     side->reqvalue = DFLTFLAG;
  290. }
  291.  
  292. /* The requester proper, which just sets up the hook to call the main */
  293. /* command interpreter when some input comes by. */
  294.  
  295. request_command(side)
  296. Side *side;
  297. {
  298.     request_input(side, side->curunit, x_command);
  299. }
  300.  
  301. /* Search in command table and execute function if found, complaining if */
  302. /* the command is not recognized.  Many commands operate on the "current */
  303. /* unit", and all uniformly error out if there is no current unit, so put */
  304. /* that test here.  Also fix up the arg if the value passed is one of the */
  305. /* specially recognized ones. */
  306.  
  307. execute_command(side, ch, n)
  308. Side *side;
  309. char ch;
  310. int n;
  311. {
  312.     struct func_tab *cmd;
  313.     
  314.     for (cmd = commands; cmd->fchar != '\0'; ++cmd) {
  315.     if (ch == cmd->fchar) {
  316.         if (n == DFLTFLAG) n = cmd->defaultarg;
  317.         if (n == NEGFLAG)  n = 0 - cmd->defaultarg;
  318.         if (n == ITERFLAG) n = side->itertime;
  319.         if (Debug) printf("... actual arg is %d\n", n);
  320.         if (cmd->needunit) {
  321.         if (side->curunit != NULL) {
  322.             (*(cmd->code))(side, side->curunit, n);
  323.         } else {
  324.             cmd_error(side, "No unit to operate on here!");
  325.         }
  326.         } else {
  327.         (*(cmd->code))(side, n);
  328.         }
  329.         return;
  330.     }
  331.     }
  332.     cmd_error(side, "Unknown command '%c'", ch);
  333. }
  334.  
  335. /* Help for the main command mode just dumps part of the table, and a little */
  336. /* extra info about what's not in the table.  This may go onto a screen or */
  337. /* into a file, depending on where this was called from. */
  338.  
  339. command_help(side)
  340. Side *side;
  341. {
  342.     FuncTab *cmd;
  343.  
  344.     wprintf(side, "To move a unit, use the mouse or [hlyubn]");
  345.     wprintf(side, "[HLYUBN] moves unit repeatedly in that direction");
  346.     wprintf(side, "Mousing unit makes it sit, mousing enemy unit attacks");
  347.     if (Build) wprintf(side, "Terrain characters set what will be painted.");
  348.     wprintf(side, "  ");
  349.     for (cmd = commands; cmd->fchar != '\0'; ++cmd) {
  350.     wprintf(side, "%c  %s", cmd->fchar, cmd->help);
  351.     }
  352. }
  353.  
  354. /* The handler for unit production needs to make sure valid input has */
  355. /* been received, will put in another request if not. */
  356.  
  357. x_product_type(side)
  358. Side *side;
  359. {
  360.     int u;
  361.  
  362.     if ((u = grok_unit_type(side)) >= 0) {
  363.     set_product(side->requnit, u);
  364.     set_schedule(side->requnit);
  365.     side->info_change = TRUE;
  366.     } else {
  367.     request_input(side, side->requnit, x_product_type);
  368.     }
  369. }
  370.  
  371. /* This is called when production is to be set or changed.  Note that since */
  372. /* the user has a pretty dull choice if there is only one possible type of */
  373. /* unit to build, in such cases we can bypass requests altogether. */
  374.  
  375. request_new_product(unit)
  376. Unit *unit;
  377. {
  378.     int u;
  379.     Side *us = unit->side;
  380.  
  381.     if (humanside(us)) {
  382.     sprintf(spbuf, "%s will build: ", unit_handle(us, unit));
  383.     u = ask_unit_type(us, spbuf, utypes[unit->type].make);
  384.     if (u < 0) {
  385.         make_current(us, unit);
  386.         request_input(us, unit, x_product_type);
  387.     } else {
  388.         set_product(unit, u);
  389.         set_schedule(unit);
  390.         us->info_change = TRUE;
  391.     }
  392.     } else {
  393.     set_product(unit, machine_product(unit));
  394.     set_schedule(unit);
  395.     }
  396. }
  397.  
  398. /* Prompt for a type of a unit from player, maybe only allowing some types */
  399. /* to be accepted.  Also allow specification of no unit type.  We do this */
  400. /* by scanning the vector, building a string of chars and a vector of */
  401. /* unit types, so as to be able to map back when done. */
  402.  
  403. ask_unit_type(side, prompt, possibles)
  404. Side *side;
  405. char *prompt;
  406. short *possibles;
  407. {
  408.     int numtypes = 0, u, type;
  409.  
  410.     for_all_unit_types(u) {
  411.     side->bvec[u] = 0;
  412.     if (possibles == NULL || possibles[u]) {
  413.         side->bvec[u] = 1;
  414.         side->ustr[numtypes] = utypes[u].uchar;
  415.         side->uvec[numtypes] = u;
  416.         numtypes++;
  417.     }
  418.     }
  419.     if (numtypes == 0) {
  420.     type = NOTHING;
  421.     } else if (numtypes == 1) {
  422.     type = side->uvec[0];
  423.     side->bvec[type] = 0;
  424.     } else {
  425.     side->ustr[numtypes] = '\0';
  426.     sprintf(side->promptbuf, "%s [%s] ", prompt, side->ustr);
  427.     show_prompt(side);
  428.     draw_unit_list(side, side->bvec);
  429.     type = -1;
  430.     }
  431.     return type;
  432. }
  433.  
  434. /* Do something with the char or unit type that the player entered. */
  435.  
  436. grok_unit_type(side)
  437. Side *side;
  438. {
  439.     int i, type = -1;
  440.  
  441.     switch (side->reqtype) {
  442.     case KEYBOARD:
  443.     echo_at_prompt(side, side->reqch);
  444.     if (side->reqch == '?') {
  445.         help_unit_type(side);
  446.     } else if (side->reqch == ESCAPE) {
  447.         type = NOTHING;
  448.     } else if (iindex(side->reqch, side->ustr) == -1) {
  449.         notify(side, "Unit type '%c' not in \"%s\"!",
  450.            side->reqch, side->ustr);
  451.     } else {
  452.         type = find_unit_char(side->reqch);
  453.     }
  454.     break;
  455.     case UNITTYPE:
  456.     if (between(0, side->requtype, period.numutypes-1) &&
  457.         side->bvec[side->requtype]) {
  458.         type = side->requtype;
  459.     } else {
  460.         notify(side, "Not a valid unit type!");
  461.     }
  462.     break;
  463.     default:
  464.     break;
  465.     }
  466.     if (type >= 0) {
  467.     clear_prompt(side);
  468.     for_all_unit_types(i) side->bvec[i] = 0;
  469.     draw_unit_list(side, side->bvec);
  470.     }
  471.     return type;
  472. }
  473.  
  474. /* User is asked to pick a position on map.  This will iterate until the */
  475. /* space bar designates the final position. */
  476.  
  477. ask_position(side, prompt)
  478. Side *side;
  479. char *prompt;
  480. {
  481.     sprintf(side->promptbuf, "%s [mouse or keys to move, space bar to set]",
  482.         prompt);
  483.     show_prompt(side);
  484.     side->tmpcurx = side->curx;  side->tmpcury = side->cury;
  485.     side->tmpcurunit = side->curunit;
  486. }
  487.  
  488. /* Restore the saved "cur" slots. */
  489.  
  490. restore_cur(side)
  491. Side *side;
  492. {
  493.     if (side->curx != side->tmpcurx || side->cury != side->tmpcury)
  494.     side->cursor_change = TRUE;
  495.     side->curx = side->tmpcurx;  side->cury = side->tmpcury;
  496.     side->curunit = side->tmpcurunit;
  497. }
  498.  
  499. /* Interpret the user's input in response to a position request.  All we */
  500. /* have to comprehend is direction keys and mouse hits.  Space bars and */
  501. /* unmoving mice both mean that a position has been decided on. */
  502.  
  503. grok_position(side)
  504. Side *side;
  505. {
  506.     int dir;
  507.  
  508.     switch (side->reqtype) {
  509.     case KEYBOARD:
  510.     if (side->reqch == ' ') {
  511.         clear_prompt(side);
  512.         return TRUE;
  513.     } else if ((dir = iindex(side->reqch, dirchars)) >= 0) {
  514.         side->reqposx = wrap(side->reqposx + dirx[dir]);
  515.         side->reqposy = side->reqposy + diry[dir];
  516.     } else if ((dir = iindex(lowercase(side->reqch), dirchars)) >= 0) {
  517.         side->reqposx = wrap(side->reqposx + 10*dirx[dir]);
  518.         side->reqposy = side->reqposy + 10*diry[dir];
  519.     }
  520.     break;
  521.     case MAPPOS:
  522.     if (side->reqx == side->reqposx && side->reqy == side->reqposy) {
  523.         clear_prompt(side);
  524.         return TRUE;
  525.     } else {
  526.         side->reqposx = side->reqx;  side->reqposy = side->reqy;
  527.     }
  528.     break;
  529.     default:
  530.     break;
  531.     }
  532.     if (side->curx != side->reqposx || side->cury != side->reqposy)
  533.     side->cursor_change = TRUE;
  534.     side->curx = side->reqposx;  side->cury = side->reqposy;
  535.     side->curunit = NULL;
  536.     show_info(side);
  537.     return FALSE;
  538. }
  539.  
  540. /* Prompt for a yes/no answer with a settable default. */
  541.  
  542. ask_bool(side, question, dflt)
  543. Side *side;
  544. char *question;
  545. bool dflt;
  546. {
  547.     sprintf(side->promptbuf, "%s [%s]", question, (dflt ? "yn" : "ny"));
  548.     show_prompt(side);
  549.     side->reqvalue2 = dflt;
  550. }
  551.  
  552. /* Figure out what the answer actually is, keeping the default in mind. */
  553.  
  554. grok_bool(side)
  555. Side *side;
  556. {
  557.     bool dflt = side->reqvalue2;
  558.     char ch = side->reqch;
  559.  
  560.     if (side->reqtype == KEYBOARD) {
  561.     if (dflt ? (lowercase(ch) == 'n') : (lowercase(ch) == 'y'))
  562.         dflt = !dflt;
  563.     }
  564.     clear_prompt(side);
  565.     return dflt;
  566. }
  567.  
  568. /* Prompt for a single character. */
  569.  
  570. ask_char(side, question, choices)
  571. Side *side;
  572. char *question, *choices;
  573. {
  574.     sprintf(side->promptbuf, "%s [%s]", question, choices);
  575.     show_prompt(side);
  576. }
  577.  
  578. /* The char has already been processed, so just pass it through. */
  579.  
  580. grok_char(side)
  581. Side *side;
  582. {
  583.     if (side->reqtype == KEYBOARD) {
  584.     clear_prompt(side);
  585.     return side->reqch;
  586.     } else {
  587.     return '\0';
  588.     }
  589. }
  590.  
  591. /* Read a string from the prompt window.  Deletion is allowed, and a */
  592. /* cursor is displayed (this should definitely be a toolkit call...) */
  593. /* Some restrictions on what strings can be read - for instance can't */
  594. /* read or default to a NULL string. */
  595.  
  596. ask_string(side, prompt, dflt)
  597. Side *side;
  598. char *prompt, *dflt;
  599. {
  600.     if (dflt == NULL) {
  601.     sprintf(side->promptbuf, "%s ", prompt);
  602.     } else {
  603.     sprintf(side->promptbuf, "%s (default \"%s\") ", prompt, dflt);
  604.     }
  605.     show_prompt(side);
  606.     side->reqstrbeg = strlen(side->promptbuf);
  607.     side->reqcurstr = side->reqstrbeg;
  608.     write_str_cursor(side);
  609.     side->reqdeflt = dflt;
  610. }
  611.  
  612. /* Dig a character from the filled-in request and add it into the string. */
  613. /* Keep returning NULL until we get something. */
  614.  
  615. char *
  616. grok_string(side)
  617. Side *side;
  618. {
  619.     if (side->reqtype == KEYBOARD) {
  620.     if (side->reqch == '\r' ||
  621.         side->reqch == '\n' ||
  622.         side->reqcurstr >= BUFSIZE-2) {
  623.         if (side->reqcurstr == side->reqstrbeg && side->reqdeflt != NULL) {
  624.         strcpy(side->promptbuf+side->reqstrbeg, side->reqdeflt);
  625.         } else {
  626.         (side->promptbuf)[side->reqcurstr] = '\0';
  627.         }
  628.         clear_prompt(side);
  629.         return copy_string(side->promptbuf + side->reqstrbeg);
  630.     } else {
  631.         if (side->reqch == BACKSPACE) {
  632.         if (side->reqcurstr > side->reqstrbeg) --side->reqcurstr;
  633.         } else {
  634.         echo_at_prompt(side, side->reqch);
  635.         (side->promptbuf)[side->reqcurstr++] = side->reqch;
  636.         }
  637.     }
  638.     write_str_cursor(side);
  639.     (side->promptbuf)[side->reqcurstr+1] = '\0';
  640.     }
  641.     return NULL;
  642. }
  643.